<!DOCTYPE html>
<html lang="zh-CN">

<!-- Head tag -->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  
  <meta name="baidu-site-verification" content="undefined" />
  <meta name="google-site-verification" content="undefined" />
  <meta name="msvalidate.01" content="undefined" />
  <meta name="360-site-verification" content="undefined" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- if page has tags, then add tags to keyword -->
  
  
  <meta name="keywords" content=",Golang汇编,Golang底层,Golang,">
  <!-- page.description has higher priority -->
  <meta name="description" content="Golang汇编第一篇文章，先介绍一下背景和指令">
  <link rel="shortcut icon" href="/blog/img/logo.webp">
  <title>
    
    Golang汇编(一)-Plan9介绍及指令详解 | Evrn
    
  </title>

  <link rel="canonical" href="/blog/post/go-asm-indroduce.html">
  
<link rel="stylesheet" href="/blog/css/reset.css">

  
<link rel="stylesheet" href="/blog/css/layout.css">

  
<link rel="stylesheet" href="/blog/css/markdown.css">

  <!-- Pygments Highlight CSS -->
  
<link rel="stylesheet" href="/blog/css/highlight.css">

  
<link rel="stylesheet" href="/blog/css/geektutu.css">

  <!-- global function -->
  <script>
    window.globalAddScript = function (url, onload, onerror) {
      var s = document.createElement('script');
      s.src = url;
      onload && (s.onload = onload);
      onerror && (s.onerror = onerror);
      document.body.appendChild(s);
    }
    window.globalAddCss = function (url) {
      var s = document.createElement('link');
      s.rel = 'stylesheet';
      s.href = url;
      document.body.appendChild(s);
    }
    window.getPosition = function (ele) {
      var x = 0, y = 0;
      while (ele) {
        x += (ele.offsetLeft - ele.scrollLeft + ele.clientLeft);
        y += (ele.offsetTop - ele.scrollTop + ele.clientTop);
        ele = ele.offsetParent;
      }
      return { x: x, y: y };
    }
    window.getDom = function (str) { return document.querySelector(str) }
  </script>
  <!-- google ad -->
  
<meta name="generator" content="Hexo 4.2.0"></head>

<body>
    <header class="gkt-header col-xs-12 padding-0">
    <div id="gkt-nav" class="gkt-header-container">
        <a href="/blog/" class="gkt-header-title float-left">
            <img class="float-left" src="/blog/img/logo.webp" alt="">
            <span>Evrn</span>
        </a>
        <nav class="gkt-header-nav text-right">
            <ul>
                <li><a class="hidden-xs" href="/blog/">主页</a></li>
                <li><a href="/blog/series/">专题</a></li>
                <li><a href="/blog/archives/">归档</a></li>
                <li><a href="/blog/post/link.html">友链</a></li>
                <li><a href="/blog/post/about.html">留言</a></li>
            </ul>
        </nav>
    </div>
    <div id="gkt-cate-nav" class="gkt-header-container hidden-xs">
        
        <nav class="gkt-header-nav float-left">
            <ul>
                
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-learn-interface.html">Golang学习</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left active">
                    <a class="float-left" href="/blog/post/go-asm-indroduce.html">Golang汇编</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/tcp-three-way-handshake.html">基础</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/go-how-to-build.html">Golang底层</a>
                    
                </li>
                
                <li class="gkt-cate-name float-left ">
                    <a class="float-left" href="/blog/post/mysql-index-use.html">MySQL</a>
                    
                </li>
                
            </ul>
        </nav>
    </div>
</header>
<div style="height: 44px"></div>
<div class="hidden-xs" style="height: 44px"></div>
<script>
    (function () {
        window.addEventListener('scroll', function () {
            if (window.innerWidth < 768) {
                return;
            }
            var nav = document.querySelector('#gkt-nav');
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            scrollTop > 50 && (nav.classList.add('hide'));
            scrollTop <= 50 && (nav.classList.remove('hide'));
        });
        var cateNavs = document.querySelectorAll('#gkt-cate-nav>nav>ul>li');
        [].slice.call(cateNavs).forEach(function (item) {
            var sub = item.querySelector('.gkt-sub-cate');
            if (!sub) return;
            item.addEventListener('mouseenter', function (e) { sub.style.display = 'block'; }, false);
            item.addEventListener('mouseleave', function (e) { sub.style.display = 'none'; }, false);
        })
    })();
</script>
    <!-- Main Content -->
    <div class="main-container">
        <!-- Main Content -->
<main class="col-xs-12 padding-0 markdown-it">
    <!-- Post Container -->
    
    
    <!-- Post Content -->
<div class="float-left post-container box-shadow">
    <div class="u-arrow-wrapper hidden-xs">
        
        <a class="float-left" href="/blog/post/go-method-params.html"><i class="u-arrow-left"></i></a>
        
        
        <a class="float-right" href="/blog/post/go-asm-method.html"><i class="u-arrow-right"></i></a>
        
    </div>
    <article class="col-xs-12">
        <h1> Golang汇编(一)-Plan9介绍及指令详解 </h1>

        

        
        <div class="hidden-lg hidden-md series_links">
            <p> <strong> Golang汇编系列文章链接：</strong></p>
            <ul>
                
                <li>
                    <a href="/blog/post/go-asm-indroduce.html">Golang汇编(一)-Plan9介绍及指令详解</a>
                    <span class="post-item-date">(Feb 6, 2020)</span>
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-method.html">Golang汇编(二)-函数调用</a>
                    <span class="post-item-date">(Feb 16, 2020)</span>
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-my-asm.html">Golang汇编(三)-编写自己的汇编代码</a>
                    <span class="post-item-date">(Feb 19, 2020)</span>
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-datastruct.html">Golang汇编(四)-汇编眼中的数据结构</a>
                    <span class="post-item-date">(Feb 19, 2020)</span>
                </li>
                
            </ul>
        </div>
        
        
        
        <p>在深入源码层面研究golang的时候无可避免的要涉及到汇编语言，利用它可以知道golang很多底层的实现，本文就来简单介绍一下golang的汇编已经常见的指令。</p>
<p>本文目的是学习后能看懂大部分golang反编译出来的代码，对很多原理性的东西不会深究。</p>
<p>同时由于汇编涉及的指令很多，本文不定期更新。</p>
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>Go 使用的汇编是 <a href="https://9p.io/sys/doc/asm.html" target="_blank" rel="noopener">plan9 汇编</a>。</p>
<p>plan9 的汇编的操作数的方向是和 intel 汇编相反的，与 <a href="https://quasilyte.dev/blog/post/go-asm-complementary-reference/#external-resources" target="_blank" rel="noopener">AT&amp;T</a> 类似。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOVQ $0x10, AX &#x3D;&#x3D;&#x3D;&#x3D;&#x3D; mov rax, 0x10</span><br><span class="line">       |    |------------|      |</span><br><span class="line">       |------------------------|</span><br></pre></td></tr></table></figure>

<p>同样是设置AX寄存器的值为10，plan9是左边，intel汇编是右边写法，</p>
<p>反正不是从左到右就是从右到左，知道有这个不同就好了。</p>
<h2 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a>寄存器</h2><h3 id="通用寄存器"><a href="#通用寄存器" class="headerlink" title="通用寄存器"></a>通用寄存器</h3><p>Go汇编同样可以操作其他汇编能操作的通用寄存器</p>
<p>plan9 中使用寄存器不需要带 r 或 e 的前缀，例如 rax，只要写 AX 即可:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MOVQ $101, AX &#x3D; mov rax, 101</span><br></pre></td></tr></table></figure>

<p>下面是通用通用寄存器的名字在 X64 和 plan9 中的对应关系:</p>
<table>
<thead>
<tr>
<th>X64</th>
<th>rax</th>
<th>rbx</th>
<th>rcx</th>
<th>rdx</th>
<th>rdi</th>
<th>rsi</th>
<th>rbp</th>
<th>rsp</th>
<th>r8</th>
<th>r9</th>
<th>r10</th>
<th>r11</th>
<th>r12</th>
<th>r13</th>
<th>r14</th>
<th>rip</th>
</tr>
</thead>
<tbody><tr>
<td>Plan9</td>
<td>AX</td>
<td>BX</td>
<td>CX</td>
<td>DX</td>
<td>DI</td>
<td>SI</td>
<td>BP</td>
<td>SP</td>
<td>R8</td>
<td>R9</td>
<td>R10</td>
<td>R11</td>
<td>R12</td>
<td>R13</td>
<td>R14</td>
<td>PC</td>
</tr>
</tbody></table>
<ul>
<li><strong>硬件SP</strong>：指示函数栈的栈顶</li>
<li><strong>硬件BP</strong>：指示函数栈的起始位置(栈底)，栈起始的位置</li>
</ul>
<h3 id="伪寄存器"><a href="#伪寄存器" class="headerlink" title="伪寄存器"></a>伪寄存器</h3><p>Go 汇编引入了几个伪寄存器，这几个伪寄存器非常重要：</p>
<ul>
<li><code>FP</code>: Frame pointer: 用来访问参数和局部变量</li>
<li><code>PC</code>: Program counter:下一条将要执行指令的地址.用于分支和跳转，</li>
<li><code>SB</code>: Static base pointer: 全局符号集.一般用于声明函数或者全局变量</li>
<li><code>SP</code>: Stack pointer: top of stack.指向当前栈帧的局部变量的开始位置，一般用来引用函数的局部变量</li>
</ul>
<h2 id="指令介绍"><a href="#指令介绍" class="headerlink" title="指令介绍"></a>指令介绍</h2><p>使用汇编就意味着丧失了跨平台的特性。因此使用对应平台的汇编指令。这个需要自行去了解，也可以参考<a href="https://lrita.github.io/images/posts/go/GoFunctionsInAssembly.pdf" target="_blank" rel="noopener">GoFunctionsInAssembly</a> 其中有各个平台汇编指令速览和对照。</p>
<h3 id="操作字节限制"><a href="#操作字节限制" class="headerlink" title="操作字节限制"></a>操作字节限制</h3><p>每个操作指令后面都有个后缀，指示这次操作的字节数，比如MOVQ里面，Q就是这个后缀</p>
<p>常用的后缀有</p>
<table>
<thead>
<tr>
<th>Q</th>
<th>操作64位数据（8 bytes）</th>
<th>Q =&gt; Quadword</th>
</tr>
</thead>
<tbody><tr>
<td>L</td>
<td>操作32位数据（4 bytes）</td>
<td>L =&gt; Long</td>
</tr>
<tr>
<td>W</td>
<td>操作16位数据（2 bytes）</td>
<td>W =&gt; Word</td>
</tr>
<tr>
<td>B</td>
<td>操作8位数据（1 byte）</td>
<td>B =&gt; Byte</td>
</tr>
</tbody></table>
<h3 id="MOV"><a href="#MOV" class="headerlink" title="MOV"></a>MOV</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MOVQ    AX, BX           &#x2F;&#x2F;存储AX寄存器的值到BX寄存器</span><br><span class="line">MOVUPS  X0, &quot;&quot;.d+8(SP)   &#x2F;&#x2F;连续内存的清空</span><br></pre></td></tr></table></figure>

<h3 id="ADD"><a href="#ADD" class="headerlink" title="ADD"></a>ADD</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ADDQ    AX, BX           &#x2F;&#x2F; BX&#x3D;AX+BX</span><br></pre></td></tr></table></figure>

<h3 id="SUB"><a href="#SUB" class="headerlink" title="SUB"></a>SUB</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SUBQ    AX, BX           &#x2F;&#x2F; BX&#x3D;AX-BX</span><br></pre></td></tr></table></figure>

<h3 id="XOR"><a href="#XOR" class="headerlink" title="XOR"></a>XOR</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">XORQ    AX, AX           &#x2F;&#x2F; 异或操作 AX&#x3D;AX xor AX 通常用于清空寄存器</span><br></pre></td></tr></table></figure>

<h3 id="LEA"><a href="#LEA" class="headerlink" title="LEA"></a>LEA</h3><p>地址运算也是用 lea 指令，英文原意为<code>Load Effective Address</code>，amd64 平台地址都是<code>8</code>个字节，所以直接就用<code>LEAQ</code>就好：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LEAQ    AX, BX           &#x2F;&#x2F; 存储AX的地址到BX</span><br></pre></td></tr></table></figure>



<h3 id="地址偏移"><a href="#地址偏移" class="headerlink" title="地址偏移"></a>地址偏移</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">4(AX)    &#x2F;&#x2F; 通过对AX的地址加4个字节再操作，括号前面的数字表示偏移量</span><br><span class="line">MOVQ   +8(SP),BX   &#x2F;&#x2F;对sp存储的地址+8字节所在位置的内存存储到BX</span><br><span class="line">MOVQ   -8(FP),BX   &#x2F;&#x2F;对FP存储的地址-8字节所在位置的内存存储到BX</span><br><span class="line">LEAQ   +40(SP),BX  &#x2F;&#x2F;对sp存储的地址+8字节（不解地址）存储到BX</span><br></pre></td></tr></table></figure>

<h3 id="LEA和MOV的区别"><a href="#LEA和MOV的区别" class="headerlink" title="LEA和MOV的区别"></a>LEA和MOV的区别</h3><p>MOV 和 LEA 的作用都是把结果存储到目标寄存器，区别就是如果操作的是地址MOV会对地址进行二次寻址，查找地址指向内存的内容，而LEA不解地址，直接将地址放到目标寄存器。一般用于操作FP,SP,BP等地址寄存器.</p>
<p>如以下代码，会报错，</p>
<p><code>panic: runtime error: invalid memory address or nil pointer dereference</code></p>
<p>因为地址为1的指向的内存是非法区域</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MOVQ $1,BX</span><br><span class="line">MOVQ (BX),AX</span><br></pre></td></tr></table></figure>

<p>但是以下代码就把1放到AX寄存器</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MOVQ $1,BX</span><br><span class="line">LEAQ (BX),AX</span><br></pre></td></tr></table></figure>

<p>LEA还可以对寄存器的值做一些算术处理</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">MOVQ $3,BX</span><br><span class="line">MOVQ $2,AX</span><br><span class="line">LEAQ (BX)(AX*8), CX  &#x2F;&#x2F; &#x3D;&gt; CX &#x3D;BX + (AX * 8)&#x3D;3+2*8&#x3D;19</span><br><span class="line">&#x2F;&#x2F; 上面代码中的 8 代表 scale</span><br><span class="line">&#x2F;&#x2F; scale 只能是 1、2、4、8</span><br><span class="line">&#x2F;&#x2F; 如果写成其它值:</span><br><span class="line">&#x2F;&#x2F; LEAQ (BX)(AX*3), CX</span><br><span class="line">&#x2F;&#x2F; .&#x2F;a.s:6: bad scale: 3</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 如果要表示3倍的乘法可以表示为:</span><br><span class="line">LEAQ (AX)(AX*2), CX &#x2F;&#x2F; &#x3D;&gt; CX &#x3D; AX + (AX * 2) &#x3D; AX * 3 &#x3D;6</span><br><span class="line"></span><br><span class="line">LEAQ 4(AX)(BX*4), CX &#x2F;&#x2F; &#x3D;&gt; CX &#x3D; (AX+4) + (BX * 4) &#x3D; 6+12 &#x3D; 18</span><br></pre></td></tr></table></figure>

<h3 id="跳转指令"><a href="#跳转指令" class="headerlink" title="跳转指令"></a>跳转指令</h3><p>汇编中的跳转主要分为有条件跳转和无条件跳转</p>
<h4 id="无条件跳转"><a href="#无条件跳转" class="headerlink" title="无条件跳转"></a>无条件跳转</h4><p>就是跳转到指定label的位置</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">	JMP end &#x2F;&#x2F;跳转到label end的位置</span><br><span class="line">...</span><br><span class="line">end:</span><br><span class="line">	MOVQ $1,ret+0(FP)</span><br><span class="line">	RET</span><br></pre></td></tr></table></figure>

<h4 id="有条件跳转"><a href="#有条件跳转" class="headerlink" title="有条件跳转"></a>有条件跳转</h4><p>一般结合CMP指令来使用</p>
<p>cmp指令，通过对两个操作数相减，影响<strong>置零位</strong>和<strong>借位</strong></p>
<p>然后通过JXX指令通过检查<strong>置零位</strong>和<strong>借位</strong>决定是否跳转</p>
<p>如JNE/JNZ 判断置零位ZF=0 就跳转到标签处</p>
<p>ZF=0 代表相减的结果不等于0，说明两个操作数不相等</p>
<p>因此，JNE就代表 两个操作数不相等就跳转</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CMPQ	AX,$1</span><br><span class="line">JNE	  end   &#x2F;&#x2F;如果AX里面的值不等于1，则跳转到end处</span><br></pre></td></tr></table></figure>

<h4 id="汇编语言中的标志位：CF、PF、AF、ZF、SF、TF、IF、DF、OF"><a href="#汇编语言中的标志位：CF、PF、AF、ZF、SF、TF、IF、DF、OF" class="headerlink" title="汇编语言中的标志位：CF、PF、AF、ZF、SF、TF、IF、DF、OF"></a>汇编语言中的标志位：CF、PF、AF、ZF、SF、TF、IF、DF、OF</h4><p><strong>CF</strong>:进位标志位。在无符号运算时，记录了运算结果的最高有效位向更高位的进位值或从更高位借位，产生进位或借位时CF=1，否则CF=0；</p>
<p><strong>PF:</strong>奇偶标志位。相关指令执行后结果所有bit中1的个数为偶数，那么PF=1，1的个数为奇数则PF=0；</p>
<p><strong>AF:</strong>辅助进位标志位。运算过程中看最后四位，不论长度为多少。最后四位向前有进位或者借位，AF=1,否则AF=0;</p>
<p><strong>ZF:</strong>零标志位。相关指令执行后结果为0那么ZF=1,结果不为0则ZF=0；</p>
<p><strong>SF:</strong>符号标志位。相关指令执行后结果为负那么SF=1，结果非负数则SF=0；</p>
<p><strong>TF:</strong>调试标志位。当TF=1时，处理器每次只执行一条指令，即单步执行;</p>
<p><strong>IF:</strong>中断允许标志位。它用来控制8086是否允许接收外部中断请求。若IF=1，8086能响应外部中断，反之则屏蔽外部中断;</p>
<p><strong>DF:</strong>方向标志位。在串处理指令中，每次操作后，如果DF=0，si、di递增，如果DF=1，si、di递减；注意此处DF的值是由程序员进行设定的 cld命令是将DF设置为0，std命令是将DF设置为1；</p>
<p><strong>OF:</strong>溢出标志位。记录了有符号运算的结果是否发生了溢出，如果发生溢出OF=1,如果没有OF=0；</p>
<h4 id="其他跳转指令"><a href="#其他跳转指令" class="headerlink" title="其他跳转指令"></a>其他跳转指令</h4><table>
<thead>
<tr>
<th>指令</th>
<th>转移条件</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td><strong>JC DEST</strong></td>
<td><strong>CF=1</strong></td>
<td><strong>有进位</strong>/<strong>借位</strong></td>
</tr>
<tr>
<td><strong>JNC DEST</strong></td>
<td><strong>CF=0</strong></td>
<td><strong>无进位</strong>/<strong>借位</strong></td>
</tr>
<tr>
<td><strong>JE/JZ DEST</strong></td>
<td><strong>ZF=1</strong></td>
<td><strong>相等</strong>/<strong>等于零</strong></td>
</tr>
<tr>
<td><strong>JNE/JNZ DEST</strong></td>
<td><strong>ZF=0</strong></td>
<td><strong>不相等</strong>/<strong>不等于零</strong></td>
</tr>
<tr>
<td><strong>JS DEST</strong></td>
<td><strong>SF=1</strong></td>
<td><strong>是负数</strong></td>
</tr>
<tr>
<td><strong>JNS DEST</strong></td>
<td><strong>SF=0</strong></td>
<td><strong>是正数</strong></td>
</tr>
<tr>
<td><strong>JO DEST</strong></td>
<td><strong>OF=1</strong></td>
<td><strong>有溢出</strong></td>
</tr>
<tr>
<td><strong>JNO DEST</strong></td>
<td><strong>OF=0</strong></td>
<td><strong>无溢出</strong></td>
</tr>
<tr>
<td><strong>JP/JPE DEST</strong></td>
<td><strong>PF=1</strong></td>
<td><strong>有偶数个</strong>“<strong>1</strong>”</td>
</tr>
<tr>
<td><strong>JNP/JPO DEST</strong></td>
<td><strong>PF=0</strong></td>
<td><strong>有奇数个</strong>“<strong>1</strong>”</td>
</tr>
</tbody></table>
<p>（2）根据两个无符号数的比较结果判断转移的指令 v</p>
<table>
<thead>
<tr>
<th>指令</th>
<th>转移条件</th>
<th>含义</th>
<th>英文表述</th>
</tr>
</thead>
<tbody><tr>
<td>JA/JNBE DEST</td>
<td>CF=0 AND ZF=0</td>
<td>无符号数A&gt;B</td>
<td>JMP above (not below or equal)</td>
</tr>
<tr>
<td>JAE/JNB DEST</td>
<td>CF=0</td>
<td>无符号数A≥B</td>
<td>JMP above or equal (not below )</td>
</tr>
<tr>
<td>JB/JNAE DEST</td>
<td>CF=1</td>
<td>无符号数A&lt;B</td>
<td>JMP blow (not above or equal)</td>
</tr>
<tr>
<td>JBE/JNA DEST</td>
<td>CF=1 OR ZF=1</td>
<td>无符号数A≤B</td>
<td>JMP blow or equal (not above)</td>
</tr>
</tbody></table>
<p>（3）根据两个有符号数的比较结果判断转移的指令</p>
<table>
<thead>
<tr>
<th>指令</th>
<th>转移条件</th>
<th>含义</th>
<th>英文表述</th>
</tr>
</thead>
<tbody><tr>
<td>JG/JNLE DEST</td>
<td>SF=OF AND ZF=0</td>
<td>有符号数A&gt;B</td>
<td>JMP greater (not less or equal)</td>
</tr>
<tr>
<td>JGE/JNL DEST</td>
<td>SF=OF OR ZF=1</td>
<td>有符号数A≥B</td>
<td>JMP greater or equal (not less)</td>
</tr>
<tr>
<td>JL/JNGE DEST</td>
<td>SF≠OF AND ZF=0</td>
<td>有符号数A&lt;B</td>
<td>JMP less (not greater or equal)</td>
</tr>
<tr>
<td>JLE/JNG DEST</td>
<td>SF≠OF OR ZF=1</td>
<td>有符号数A≤B</td>
<td>JMP less or equal (not greater)</td>
</tr>
</tbody></table>
<h4 id="Go中跳转指令映射"><a href="#Go中跳转指令映射" class="headerlink" title="Go中跳转指令映射"></a>Go中跳转指令映射</h4><p>在go汇编里面有些跳转并不是按照上面的标准命名，但是实现的效果是一样的，需要注意对应的映射</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">JCC   &#x3D;&gt;  JAE</span><br><span class="line">JCS   &#x3D;&gt;  JB</span><br><span class="line">JCXZL &#x3D;&gt;  JECXZ</span><br><span class="line">JEQ   &#x3D;&gt;  JE,JZ</span><br><span class="line">JGE   &#x3D;&gt;  JGE</span><br><span class="line">JGT   &#x3D;&gt;  JG</span><br><span class="line">JHI   &#x3D;&gt;  JA</span><br><span class="line">JLE   &#x3D;&gt;  JLE</span><br><span class="line">JLS   &#x3D;&gt;  JBE</span><br><span class="line">JLT   &#x3D;&gt;  JL</span><br><span class="line">JMI   &#x3D;&gt;  JS</span><br><span class="line">JNE   &#x3D;&gt;  JNE, JNZ</span><br><span class="line">JOC   &#x3D;&gt;  JNO</span><br><span class="line">JOS   &#x3D;&gt;  JO</span><br><span class="line">JPC   &#x3D;&gt;  JNP, JPO</span><br><span class="line">JPL   &#x3D;&gt;  JNS</span><br><span class="line">JPS   &#x3D;&gt;  JP, JPE</span><br></pre></td></tr></table></figure>



<h2 id="文件命名"><a href="#文件命名" class="headerlink" title="文件命名"></a>文件命名</h2><p>使用到汇编时，即表明了所写的代码不能够跨平台使用，因此需要针对不同的平台使用不同的汇编 代码。go编译器采用文件名中加入平台名后缀进行区分。</p>
<p>比如<code>sqrt_386.s  sqrt_amd64p32.s  sqrt_amd64.s  sqrt_arm.s</code></p>
<p>或者使用<code>+build tag</code>也可以，详情可以参考<a href="https://golang.org/pkg/go/build/" target="_blank" rel="noopener">go/build</a>。</p>
<h2 id="函数声明"><a href="#函数声明" class="headerlink" title="函数声明"></a>函数声明</h2><p>首先我们先需要对go汇编代码有一个抽象的认识，因此我们可以先看一段go汇编代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">TEXT runtime·profileloop(SB),NOSPLIT,$8-16</span><br><span class="line">  MOVQ    $runtime·profileloop1(SB), CX</span><br><span class="line">  MOVQ    CX, 0(SP)</span><br><span class="line">  CALL    runtime·externalthreadhandler(SB)</span><br><span class="line">  RET</span><br></pre></td></tr></table></figure>

<p>此处声明了一个函数<code>profileloop</code>，函数的声明以<code>TEXT</code>标识开头，以<code>${package}·${function}</code>为函数名。 如何函数属于本package时，通常可以不写<code>${package}</code>，只留<code>·${function}</code>即可。<code>·</code>在mac上可以用<code>shift+option+9</code> 打出。<code>$8</code>表示该函数栈大小为8byte，计算栈大小时，需要考虑局部变量和本函数内调用其他函数时，需要传参的空间，不含函数返回地址和<code>CALLER BP</code>（这2个后面会讲到）。 <code>$16</code>表示该函数入参和返回值一共有16byte。当有<code>NOSPLIT</code>标识时，可以不写输入参数、返回值占用的大小。</p>
<p><img src="http://river.marvelthanos.com/blog/20200302/J3COzsYjIaJj.png?imageslim" alt="mark"></p>
<p>那我们再看一个函数：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TEXT ·add(SB),$0-24</span><br><span class="line">  MOVQ x+0(FP), BX</span><br><span class="line">  MOVQ y+8(FP), BP</span><br><span class="line">  ADDQ BP, BX</span><br><span class="line">  MOVQ BX, ret+16(FP)</span><br><span class="line">  RET</span><br></pre></td></tr></table></figure>

<p>该函数等同于：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">func add(x, y int64) int64 &#123;</span><br><span class="line">    return x + y</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>该函数没有局部变量，故<code>$</code>后第一个数为0，但其有2个输入参数，1个返回值，每个值占8byte，则第二个数为24(3*8byte)。</p>
<h2 id="全局变量声明"><a href="#全局变量声明" class="headerlink" title="全局变量声明"></a>全局变量声明</h2><p>以下就是一个私有全局变量的声明，<code>&lt;&gt;</code>表示该变量只在该文件内全局可见。 全局变量的数据部分采用<code>DATA    symbol+offset(SB)/width, value</code>格式进行声明。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">DATA divtab&lt;&gt;+0x00(SB)&#x2F;4, $0xf4f8fcff  &#x2F;&#x2F; divtab的前4个byte为0xf4f8fcff</span><br><span class="line">DATA divtab&lt;&gt;+0x04(SB)&#x2F;4, $0xe6eaedf0  &#x2F;&#x2F; divtab的4-7个byte为0xe6eaedf0</span><br><span class="line">...</span><br><span class="line">DATA divtab&lt;&gt;+0x3c(SB)&#x2F;4, $0x81828384  &#x2F;&#x2F; divtab的最后4个byte为0x81828384</span><br><span class="line">GLOBL divtab&lt;&gt;(SB), RODATA, $64        &#x2F;&#x2F; 全局变量名声明，以及数据所在的段&quot;RODATA&quot;，数据的长度64byte</span><br><span class="line">GLOBL runtime·tlsoffset(SB), NOPTR, $4 &#x2F;&#x2F; 声明一个全局变量tlsoffset，4byte，没有DATA部分，因其值为0。</span><br><span class="line">                                       &#x2F;&#x2F; NOPTR 表示这个变量数据中不存在指针，GC不需要扫描。</span><br></pre></td></tr></table></figure>

<p>类似<code>RODATA</code>/<code>NOPTR</code>的特殊声明还有：</p>
<ul>
<li><p>NOPROF = 1 (TEXT项使用.) 不优化NOPROF标记的函数。这个标志已废弃。</p>
</li>
<li><p>DUPOK = 2 在二进制文件中允许一个符号的多个实例。链接器会选择其中之一。</p>
</li>
<li><p>NOSPLIT = 4 (TEXT项使用.) 不插入预先检测是否栈空间是否会超限，从而插入拓展栈的代码。由编译器判断，如果确定这个函数调用栈帧小于阀值则无需加相关检测代码。</p>
</li>
<li><p>RODATA = 8 (DATA和GLOBAL项使用.) 将这个数据放在只读的块中。</p>
</li>
<li><p>NOPTR = 16 这个数据不包含指针所以就不需要垃圾收集器来扫描。</p>
</li>
<li><p>WRAPPER = 32 (For TEXT items.) This is a wrapper function and should not count as disabling recover.</p>
</li>
<li><p>NEEDCTXT 标识闭包</p>
</li>
</ul>
<h2 id="局部变量声明"><a href="#局部变量声明" class="headerlink" title="局部变量声明"></a>局部变量声明</h2><p>局部变量存储在函数栈上，因此不需要额外进行声明，在函数栈上预留出空间，使用命令操作这些内存即可。因此这些 局部变量没有标识，操作时，牢记局部变量的分布、内存偏移即可。</p>
<h3 id="宏"><a href="#宏" class="headerlink" title="宏"></a>宏</h3><p>在汇编文件中可以定义、引用宏。通过<code>#define get_tls(r)  MOVQ TLS, r</code>类似语句来定义一个宏，语法结构与C语言类似；通过<code>#include &quot;textflag.h&quot;</code>类似语句来引用一个外部宏定义文件。</p>
<p>go编译器为了方便汇编中访问<code>struct</code>的指定字段，会在编译过程中自动生成一个<code>go_asm.h</code>文件，可以通过<code>#include &quot;go_asm.h&quot;</code>语言来引用，该文件中会生成该包内全部<code>struct</code>的每个字段的偏移量宏定义与结构体大小的宏定义，比如：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> vdsoVersionKey <span class="keyword">struct</span> &#123;</span><br><span class="line">	version <span class="keyword">string</span></span><br><span class="line">	verHash <span class="keyword">uint32</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>会生成宏定义：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">#define vdsoVersionKey__size 24</span><br><span class="line">#define vdsoVersionKey_version 0</span><br><span class="line">#define vdsoVersionKey_verHash 16</span><br></pre></td></tr></table></figure>

<p>在汇编代码中，我们就可以直接使用这些宏：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MOVQ vdsoVersionKey_version(DX) AX</span><br><span class="line">MOVQ (vdsoVersionKey_version+vdsoVersionKey_verHash)(DX) AX</span><br></pre></td></tr></table></figure>

<p>比如我们在<code>runtime</code>包中经常会看见一些代码就是如此：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOVQ    DX, m_vdsoPC(BX)</span><br><span class="line">LEAQ    ret+0(SP), DX</span><br><span class="line">MOVQ    DX, m_vdsoSP(BX)</span><br></pre></td></tr></table></figure>

<p>我们可以通过命令<code>go tool compile -S -asmhdr dump.h *.go</code>来导出相关文件编译过程中会生成的宏定义。</p>
<h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://github.com/cch123/golang-notes/blob/master/assembly.md#map" target="_blank" rel="noopener">plan9 assembly 完全解析</a></p>
<p><a href="https://lrita.github.io/2017/12/12/golang-asm" target="_blank" rel="noopener">golang 汇编</a></p>
<p><a href="https://9p.io/sys/doc/asm.html" target="_blank" rel="noopener">https://9p.io/sys/doc/asm.html</a></p>

        
<script src="/blog/js/qrious.min.js"></script>


        <hr>
        <div>
            
            <p>
                <span>专题: </span>
                
                <a href="/blog/series/#Golang汇编">
                    <code key="Golang汇编" class="post-label">Golang汇编</code>
                </a>
                
            </p>
            
            <p>
                <span>本文发表于 2020-02-06，最后修改于 2020-03-13。</span>
            </p>
            <!-- 文章末尾的提示 start -->
            
            
            
                
            <!-- 文章末尾的提示 end -->
        </div>
        <hr />
        <p name="pagination" style="font-size: 1.2em">
    
    <a class="float-left" href="/blog/post/go-method-params.html">上一篇 « 从切片修改引发的go参数传递思考</a>
    
    
    <a class="float-right" href="/blog/post/go-asm-method.html">下一篇 » Golang汇编(二)-函数调用</a>
    
</p>
    </article>
    <!-- 赞赏 -->
    <!--打赏-->

    <!-- 推荐阅读三篇文章 -->
    <div class="col-xs-12">
        <h3>推荐阅读</h3>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang源码"
                    title="Golang源码">Golang源码</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-learn-lib-sorts.html" class="title">
                Go源码学习 sort包
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-03-11，</span>
                    <span class="hidden-xs">全文8862字，</span>
                    <span>阅读约30分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/go.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang底层"
                    title="Golang底层">Golang底层</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-how-to-build.html" class="title">
                Golang程序编译执行流程
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-03-01，</span>
                    <span class="hidden-xs">全文7487字，</span>
                    <span>阅读约25分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
        <div class="post-preview">
    <div class="post-img" style="background-image: url('/blog/img/goasm/3.webp')"></div>
    <div class="post-info">
        <div class="post-info-center">
            <div class="hidden-xs">
                
                
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang汇编"
                    title="Golang汇编">Golang汇编</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang底层"
                    title="Golang底层">Golang底层</a>
                <span>/</span>
                
                <a class="text-gray" href="/blog/tags/#Golang"
                    title="Golang">Golang</a>
                <span>/</span>
                
                
            </div>
            <a href="/blog/post/go-asm-my-asm.html" class="title">
                Golang汇编(三)-编写自己的汇编代码
            </a>
            <p class="text-gray">
                <small>
                    <span>发表于2020-02-19，</span>
                    <span class="hidden-xs">全文8065字，</span>
                    <span>阅读约27分钟</span>
                </small>
            </p>
        </div>
    </div>
</div>
        
    </div>
    <div class="col-xs-12">
        <!-- 标签列表 -->
        <!-- Featured Tags -->
<style>
    #featured-tag .post-tag-item {
        font-size: 12px;
        line-height: 30px;
        display: inline-block;
        height: 30px;
        margin: 5px 0px;
        padding: 0 7px;
        color: #333;
        border-radius: 15px;
        background: #f6f6f6;
    }

    #featured-tag .post-tag-item:hover {
        color: #337ab7;
    }
</style>
<div id="featured-tag">
    
    <a class="post-tag-item" href="/blog/tags/#关于我" title="关于我"
        rel="1">#关于我 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#友链" title="友链"
        rel="1">#友链 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang汇编" title="Golang汇编"
        rel="4">#Golang汇编 (4) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang底层" title="Golang底层"
        rel="5">#Golang底层 (5) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang" title="Golang"
        rel="8">#Golang (8) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang基础" title="Golang基础"
        rel="1">#Golang基础 (1) </a>
    
    <a class="post-tag-item" href="/blog/tags/#Golang源码" title="Golang源码"
        rel="2">#Golang源码 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#基础" title="基础"
        rel="2">#基础 (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#tcp/http" title="tcp/http"
        rel="2">#tcp/http (2) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL" title="MySQL"
        rel="3">#MySQL (3) </a>
    
    <a class="post-tag-item" href="/blog/tags/#MySQL优化" title="MySQL优化"
        rel="1">#MySQL优化 (1) </a>
    
</div>
    </div>
    <!-- 评论 -->
    <div class="col-xs-12">
        
    </div>
</div>
<aside class="float-left gkt-sidebar hidden-xs hidden-sm">
    <div style="clear: both"></div>
    <div class="gkt-sidebar-wrapper">
        <section class="box-shadow"><style>
    .gkt-summary {
        border: 1px solid #DDDDDD;
        border-radius: 3px;
        padding: 5px;
        width: 100%;
    }


    .gkt-summary nav {
        overflow: hidden;
    }

    .gkt-summary nav a {
        display: inline-block;
        text-align: center;
        color: #333;
        font-size: 12px;
    }

    .gkt-summary nav span {
        display: block;
    }

    .gkt-summary nav .middle {
        border-left: 1px solid #eaecef;
        border-right: 1px solid #eaecef;
    }

    .gkt-summary .number {
        font-weight: bold;
    }

    .gkt-summary .link-list {
        margin-top: 5px;
        margin-bottom: -5px;
        padding-top: 7px;
        border-top: 1px dashed #999;
        display: flex;
    }

    .gkt-summary .link-list a {
        flex: 1;
    }

    .gkt-summary .link-list img {
        width: 25px;
        height: 25px;
    }
</style>

<div class="gkt-summary">
    <nav>
        <a href="/blog/" class="col-xs-4">
            <number class="number">15</number><span>文章</span>
        </a>
        <a href="/blog/series" class="col-xs-4 middle">
            <number class="number">5</number><span>专题</span>
        </a>
        <a href="/blog/tags" class="col-xs-4">
            <number class="number">11</number><span>标签</span>
        </a>
    </nav>

    
    <div class="link-list">
        
        <a href="https://marvelthanos.com" target="_blank" rel="noopener"><img src="/blog/img/related_links/thanos.webp" /></a>
        
    </div>
    
</div></section>
        
        
        <section class="gkt-sidebar-content box-shadow">
            <strong>Golang汇编</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-asm-indroduce.html"
                        class="gkt-sidebar-active">Golang汇编(一)-Plan9介绍及指令详解</a>
                    
                    <!-- Table of Contents -->
<div id="sidebar-toc">
  <!-- TOC  -->
  
  <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#介绍"><span class="toc-nav-text">介绍</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#寄存器"><span class="toc-nav-text">寄存器</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#指令介绍"><span class="toc-nav-text">指令介绍</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#文件命名"><span class="toc-nav-text">文件命名</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#函数声明"><span class="toc-nav-text">函数声明</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#全局变量声明"><span class="toc-nav-text">全局变量声明</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#局部变量声明"><span class="toc-nav-text">局部变量声明</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#参考文章"><span class="toc-nav-text">参考文章</span></a></li></ol>
  
</div>

<script>
  (function () {
    var h2 = document.querySelectorAll('article h2');
    var h3 = document.querySelectorAll('article h3');
    var linkList = document.querySelectorAll('#sidebar-toc a');

    function findLinkElement(name) {
      for (var i = 0; i < linkList.length; i++) {
        var items = linkList[i].href.split('#');
        if (items && items[items.length - 1] === encodeURIComponent(name)) {
          return i;
        }
      }
      return -1;
    }

    function activeLink(titleList) {
      var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
      for (var i = titleList.length - 1; i >= 0; i--) {
        if (scrollTop - titleList[i].offsetTop > 0) {
          var index = findLinkElement(titleList[i].id);
          index != -1 && linkList[index].classList.add('gkt-sidebar-active');
          break;
        }
      }
    }

    window.addEventListener("scroll", function (e) {
      [].slice.call(linkList).forEach(function (link) {
        link.classList.remove('gkt-sidebar-active');
      })
      activeLink(h2);
    })
  })();
</script>
                    
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-method.html"
                        class="">Golang汇编(二)-函数调用</a>
                    
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-my-asm.html"
                        class="">Golang汇编(三)-编写自己的汇编代码</a>
                    
                </li>
                
                <li>
                    <a href="/blog/post/go-asm-datastruct.html"
                        class="">Golang汇编(四)-汇编眼中的数据结构</a>
                    
                </li>
                
            </ul>
        </section>
        
        
        <section class="box-shadow">
            <strong>最近的文章</strong>
            <ul>
                
                <li>
                    <a href="/blog/post/go-learn-lib-sorts.html">Go源码学习 sort包</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-optimize-explain.html">EXPLAIN-执行计划查询命令</a>
                </li>
                
                <li>
                    <a href="/blog/post/mysql-index-base.html">《高性能MySQL》笔记-索引基础</a>
                </li>
                
            </ul>
        </section>
        <section class="box-shadow text-center">
            
        </section>
    </div>
</aside>

<script>
    (function () {
        var ele = getDom('.gkt-sidebar-content')
        var wrapper = getDom('.gkt-sidebar-wrapper')
        var last = 0
        window.addEventListener("scroll", function (e) {
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            var isDown = scrollTop > last;
            var pos = getPosition(ele).y - scrollTop;
            var downLimit = 50;
            var upLimit = -100;
            // uarrow.style.marginTop = scrollTop + 'px';
            isDown && pos <= downLimit && wrapper.classList.add("gkt-sidebar-fixed");
            !isDown && pos > upLimit && wrapper.classList.remove("gkt-sidebar-fixed");
            last = scrollTop
        })
    })();
    (function () {
        function resizeUArrow() {
            var s = getDom('.u-arrow-wrapper').style
            var pc = getDom('.post-container')
            s.left = getPosition(pc).x + 'px';
            s.width = pc.clientWidth + 'px';
        }
        resizeUArrow()
        window.addEventListener('resize', resizeUArrow);
    })();
</script>

    
</main>
    </div>
    <style>
    img#go-top {
        position: fixed;
        bottom: 100px;
        width: 50px;
        cursor: pointer;
        z-index: 9999;
    }
</style>
<img id="go-top" src="/blog/icon/top.png" class="hidden-xs" style="display: none" />
<script>
    (function () {
        var goTop = document.getElementById('go-top');
        var mainContainer = document.querySelector('.main-container');
        
        goTop.addEventListener('click', function () {
            window.scroll(0, 0);
        }, false);
        window.addEventListener('scroll', function () {
            var right = document.body.offsetWidth - mainContainer.getBoundingClientRect().right;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            goTop.style.right = right + 10 + 'px'
            scrollTop > 700 && (goTop.style.display = "block");
            scrollTop <= 700 && (goTop.style.display = "none");
        });
    })();
</script>
    <style>
    #geektutu-click-img-container {
        position: fixed;
        left: 0;
        top: 0;
        text-align: center;
        width: 100%;
        display: none;
        z-index: 9999;
    }

    #geektutu-click-img-container img {
        object-fit: contain;
        background: #eaecef;
        padding: 15px;
        border-radius: 10px;
        height: auto;
        width: auto;
        vertical-align: middle;
    }
</style>


<div id="geektutu-click-img-container">
    <img src="" alt="Big Image">
</div>

<script>
    (function () {
        var container = document.querySelector('#geektutu-click-img-container')
        var targetImg = container.querySelector('img')
        var imgs = document.querySelectorAll('article img');
        targetImg.addEventListener('click', function (e) {
            container.style.display = 'none';
            e && e.stopPropagation && e.stopPropagation();
        }, false);

        for (var i = 0; i < imgs.length; ++i) {
            var img = imgs[i];
            img.addEventListener('click', (function (src, rate) {
                return function (e) {
                    e && e.stopPropagation && e.stopPropagation();
                    if (window.innerWidth < 980) {
                        return
                    }
                    targetImg.style.height = targetImg.style.width = 'auto';
                    if (window.innerWidth / window.innerHeight > rate) {
                        targetImg.style.height = (window.innerHeight - 20) + 'px';
                    } else {
                        targetImg.style.width = (window.innerWidth - 20) + 'px';
                    }
                    container.style.height = window.innerHeight + 'px'
                    container.style.lineHeight = window.innerHeight + 'px'
                    container.style.display = 'block';
                    targetImg.src = src;
                };
            }(img.src, img.width / img.height)), false)
        }
    })();
</script>
    <!-- Footer -->
    <!-- Footer -->
<style>
    footer {
        width: 100%;
        line-height: 1.5;
        padding: 20px;
    }

    footer a {
        color: #333;
        text-decoration: none;
    }

    .footer-hexo img {
        height: 20px;
        margin-bottom: -5px;
    }

    .footer-hexo a {
        color: #337ab7;
    }
</style>
<footer class="text-center col-xs-12">
    <p>
        <small>© 2020 - Evrn - </small>
        <small>
            <a target="_blank" rel="nofollow noopener" href="http://www.miitbeian.gov.cn/"></a>
        </small>
    </p>
    <p class="footer-hexo">
        <!-- 但若直接使用或修改主题，请务必保留这段声明 -->
        <small>Powered by <a target="_blank" href="https://hexo.io">Hexo</a> | Theme
            <a target="_blank" href="https://geektutu.com">Geektutu</a>
            <a href="https://github.com/geektutu/hexo-theme-geektutu" target="_blank" rel="noopener">
                <img src=""
                    alt="Github Star">
            </a>
        </small>
    </p>
    
    <p>
        <small>
            <span id="busuanzi_container_site_pv">👁<span id="busuanzi_value_site_pv"></span></span> &nbsp;
            <span id="busuanzi_container_page_pv">📚<span id="busuanzi_value_page_pv"></span></span>
        </small>
    </p>
    
    

</footer>


<script>
    window.addEventListener('load', function () {
        globalAddScript('//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js');
    })
</script>

<!-- Baidu Tongji -->





    <script>
        // 非本站网址，新开tab页签
        (function () {
            var stopBubble = function (e) {
                e && e.stopPropagation && e.stopPropagation();
            }
            var links = document.querySelectorAll('a');
            [].slice.call(links).forEach(function (item) {
                if (item.href && item.href.indexOf(window.location.host) === -1) {
                    item.target = '_blank'
                }
                // 阻止冒泡，不触发彩蛋。
                item.addEventListener('click', stopBubble, false);
            });
            var article = document.querySelector('article');
            article && article.addEventListener('click', stopBubble, false)
        })();
    </script>
    
</body>

</html>